package elephant.job;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import elephant.Container;
import elephant.Lifecycle;
import elephant.util.ObjectPrinter;

/**
 * 
 * @author icecooly
 *
 */
public class JobManager implements Lifecycle{
	
	private static Logger logger=LoggerFactory.getLogger(JobManager.class);
	//
	private Map<String, BaseJob> jobMap;
	
	public JobManager(){
		jobMap=new HashMap<>();
	}
	
	@Override
	public void init() throws Exception {
		if(logger.isInfoEnabled()){
			logger.info(getClass().getSimpleName()+" init");
		}
	}

	@Override
	public void start() throws Exception {
		if(logger.isInfoEnabled()){
			logger.info(getClass().getSimpleName()+" start");
		}
		Container.get().scheduleAtFixedRate(this::checkJob,30,30,TimeUnit.SECONDS);
	}

	@Override
	public void stop() throws Exception {
		if(logger.isInfoEnabled()){
			logger.info(getClass().getSimpleName()+" stop");
		}
	}
	//
	public void registerJob(BaseJob job){
		String id=job.getClass().getSimpleName();
		if(jobMap.containsKey(id)){
			logger.warn("job already register."+id);
			return;
		}
		JobAnno ja=(job.getClass().getAnnotation(JobAnno.class));
		if(ja==null){
			throw new IllegalArgumentException("can not find Job annotation."+id);
		}
		if(!CronExpression.isValidExpression(ja.cron())){
			throw new IllegalArgumentException("job is not valid expression."+id);
		}
		job.id=id;
		job.cronExpression=ja.cron();
		jobMap.put(id,job);
		logger.info("registerJob "+job.getId());
	}
	//
	public List<BaseJob>getJobs(){
		return new ArrayList<BaseJob>(jobMap.values());
	}
	//
	private void checkJob(){
		jobMap.values().forEach(job->{
			Date nextRunTime;
			try {
				nextRunTime = job.getNextRunDate();
				Date now=new Date();
				logger.info("<<checkJob "+job.getId()+" nextRunTime:"+nextRunTime);
				if(nextRunTime.before(now)){
					job.runJob();
				}
			} catch (Exception e) {
				logger.error(e.getMessage(),e);
			}
			
		});
	}
	//
	public boolean runJob(String id){
		BaseJob job=jobMap.get(id);
		if(job==null){
			return false;
		}
		job.runJob();
		return true;
	}
	//
	@Override
	public String dump() {
		String format="%-5s: %-10s %-20s %-30s %-30s\n";
		ObjectPrinter op=new ObjectPrinter(format);
		op.section(getClass().getSimpleName());
		if(!jobMap.isEmpty()){
			int i=1;
			op.print("#","ID","CRON","NEXT RUN","LAST RUN");	
			for(BaseJob job:jobMap.values()){
				op.print(i++,
						job.getId(),
						job.cronExpression,
						job.getNextRunDate(),
						job.getLastRunTime());
			};
		}
		return op.toString();
	}

}
